package me.w70.fps.map;

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
import java.util.Random;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;

public class MapGen {
	protected Map map;
	public MapGen(Map mapptr) {
		this.map = mapptr;
	}
	
	public void mazeprintf(List<String> TheMap, int MapX) {
		/*
		 * Prints out a maze generated by createMaze() given the maze and map x boundary.
		 */
		String tstr = "";
		int z = TheMap.size();
		int i = 0;
		int row = 1;
		while (i < z) {
		    String space = TheMap.get(i);
		    if (space == "1") {
		        tstr += space; }
		    else {
		        tstr += " "; }
		    if ((i % MapX) == 0) {
		        System.out.println(tstr);
		        tstr = "";
		        row += 1; }
		    i += 1; }
	}
	
	public void mapprintf(char[] map, int mapx) {
		System.out.println("MapX: " + this.map.getMapx());
		System.out.println("MapY: " + this.map.getMapy());
		System.out.println("StartX: " + this.map.getStartX());
		System.out.println("StartY: " + this.map.getStartY());
		System.out.print("{");
		for (int i = 0; i < map.length; i++) {
			System.out.print("\'" + map[i] + "\', ");
			if (((i % mapx) == 0) && (i != 0)) {
				System.out.print("\n");
			}
		}
		System.out.print("}");
	}
	
	
	public char[] readfile(URL filename) {
		/*
		 * Creates a game map provided a filename.
		 * Map file must follow the format in samplemap.txt.
		 */
		if (!new File(filename.getFile()).exists()) {
			System.out.println("Error: Map file does not exist!");
			return null;
		}
		BufferedReader mapfile;
		try {
			mapfile = new BufferedReader(new InputStreamReader(filename.openStream()));
		} 
		catch (FileNotFoundException e) {
			System.out.println("Error: Map file does not exist!");
			e.printStackTrace();
			return null;
		} 
		catch (IOException e) {
			System.out.println("Error fetching map file!");
			e.printStackTrace();
			return null;
		}
		// Read the map boundary data.
		try {
			// First line must be [MapData]
			if (mapfile.readLine().startsWith("[MapData]")) {
				try {
					map.setMapx(Integer.parseInt(mapfile.readLine()));
				} 
				catch (NumberFormatException e) {
					System.out.println("Error: Map file mapX is not properly formatted!");
					e.printStackTrace();
					return null;
				} 
				catch (IOException e) {
					System.out.println("Error: Map file read error!");
					e.printStackTrace();
					return null;
				}
				try {
					map.setMapy(Integer.parseInt(mapfile.readLine()));
				} 
				catch (NumberFormatException e) {
					System.out.println("Error: Map file mapY is not properly formatted!");
					e.printStackTrace();
					return null;
				} 
				catch (IOException e) {
					System.out.println("Error: Map file read error!");
					e.printStackTrace();
					return null;
				}
				if (!mapfile.readLine().startsWith("[EndMapData]")) {
					System.out.println("Map file is improperly formatted: Too many arguments in MapData!");
					return null;
				}
			}
			else {
				System.out.println("Map file is improperly formatted: Expected [MapData] on line 1.");
				return null;
			}
		} 
		catch (IOException e) {
			e.printStackTrace();
		}
		// Now fetch the starting position.
		try {
			// First line must be [StartPos]
			if (mapfile.readLine().startsWith("[StartPos]")) {
				try {
					map.setStartX(Integer.parseInt(mapfile.readLine()));
				} 
				catch (NumberFormatException e) {
					System.out.println("Error: Map file StartX is not properly formatted!");
					e.printStackTrace();
					return null;
				} 
				catch (IOException e) {
					System.out.println("Error: Map file read error!");
					e.printStackTrace();
					return null;
				}
				try {
					map.setStartY(Integer.parseInt(mapfile.readLine()));
				} 
				catch (NumberFormatException e) {
					System.out.println("Error: Map file StartY is not properly formatted!");
					e.printStackTrace();
					return null;
				} 
				catch (IOException e) {
					System.out.println("Error: Map file read error!");
					e.printStackTrace();
					return null;
				}
				if (!mapfile.readLine().startsWith("[EndStartPos]")) {
					System.out.println("Map file is improperly formatted: Too many arguments in StartPos!");
					return null;
				}
			}
			else {
				System.out.println("Map file is improperly formatted: Expected [StartPos] on line 5.");
				return null;
			}
		} 
		catch (IOException e) {
			e.printStackTrace();
		}
		// Fetch the map data.
		String tempmapdata = "";
		try {
			// First line must be [StartPos]
			if (mapfile.readLine().startsWith("[Map]")) {
				String placeholder = "";
				int idx = 0;
				// Loop til we reach [EndMapData]
				while (true) {
					placeholder = mapfile.readLine();
					// End of map data
					if (placeholder.startsWith("[EndMap]")) {
						break;
					}
					// Check the data recieved.
					else {
						// Too much data on the line
						if (placeholder.length() > map.getMapx()) {
							System.out.println("Error: Information on line " + (idx + 9) + " exceeds Map X boundary!");
							return null;
						}
						if (idx > map.getMapy()) {
							System.out.println("Error: Information on line " + (idx + 9) + " exceeds Map Y boundary!");
							return null;
						}
						tempmapdata += placeholder;
						idx++;
					}
				}
			}
			else {
				System.out.println("Map file is improperly formatted: Expected [Map] on line 9.");
				return null;
			}
		} 
		catch (IOException e) {
			e.printStackTrace();
		}
		// Now convert the map data to a string array
		char[] mapdata = new char[tempmapdata.length()];
		for (int i = 0; i < tempmapdata.length(); i++) {
			mapdata[i] = tempmapdata.charAt(i);
		}
		return mapdata;
	}
	
	public List<String> createtestmap() {
		map.setMapx(5);
		map.setMapy(5);
		List<String> TheMap = new ArrayList<String>();
		for (int i=0; i < 6; i++) {
			TheMap.add("1");
		}
		for (int i=0; i < 3; i++) {
			TheMap.add("1");
			TheMap.add("0");
			TheMap.add("0");
			TheMap.add("0");
			TheMap.add("1"); 
		}
		for (int i=0; i < 5; i++) {
			TheMap.add("1");
		}
		return TheMap;
	}
	
	public List<String> createmaze(int x, int y) {
	/*
	 * Creates a ((x * 2) + 1) by ((y * 2) + 1) maze.
	 * Recursive depth-search maze generation algorithm.
	*/ 
	    x = (x*2)+1;
	    y = (y*2)+1;
	    map.setMapx(x);
	    map.setMapy(y);
	    List<String> TheMaze = new ArrayList<String>();
	    Stack<int[]> CellStack = new Stack<int[]>();
	    int TotalCells = (x*y) - (x*2) - (y*2);
	    int CurrCell = 2 + x;
	    int VisitedCells = 1;
	    int CurrentRow = 1;

	    // Create the initial maze as a grid
	    for (int i = 1; i < y; i++) {
	        if (CurrentRow % 2 == 0) {
	            TheMaze.add("1");
	            for (int c = 0; c < (x / 2) - 1; c++) {
	                TheMaze.add("1");
	                TheMaze.add("0"); }
	            TheMaze.add("1");
	            CurrentRow += 1; }
	        else if ((CurrentRow % 2) == 1) {
	            for (int c = 0; c < x; c++) {
	                TheMaze.add("1"); }
	            CurrentRow += 1; }
	    }
	    while (VisitedCells < TotalCells) {
	    	List<String> AvailableCells = new ArrayList<String>();
	        int NorthCell = CurrCell - (x*2);
	        int SouthCell = CurrCell + (x*2);
	        int WestCell = CurrCell - 2;
	        int EastCell = CurrCell + 2;
	        
	        // If we get an exception due to the finite list, it doesn't matter.
	        try {
	            // NorthCell
	            if (CurrCell > x*2) {
	                // North wall
	                if (TheMaze.get(NorthCell - x) == "1") {
	                    // South Wall
	                    if (TheMaze.get(NorthCell + x) == "1") {
	                        // West Wall
	                        if (TheMaze.get(NorthCell - 1) == "1") {
	                            // East Wall
	                            if (TheMaze.get(NorthCell + 1) == "1") {
	                                AvailableCells.add("N"); } 
	                            } 
	                        } 
	                    }
	            }
	        }
	        catch (IndexOutOfBoundsException e) {
	            }
	        try {
	            // SouthCell
	            if (CurrCell < ((x * y) - x)) 
	                // North wall
	                if (TheMaze.get(SouthCell - x) == "1") {
	                    // South Wall
	                    if (TheMaze.get(SouthCell + x) == "1") {
	                        // West Wall
	                        if (TheMaze.get(SouthCell - 1) == "1") {
	                            // East Wall
	                            if (TheMaze.get(SouthCell + 1) == "1") {
	                                AvailableCells.add("S"); }
	                        }
	                    }
	                }
	        }
	        catch (IndexOutOfBoundsException e) {
	            }
	        try {
	            // WestCell
	            if ((CurrCell % x) > 2) {
	                // North wall
	                if (TheMaze.get(WestCell - x) == "1") {
	                    // South Wall
	                    if (TheMaze.get(WestCell + x) == "1") {
	                        // West Wall
	                        if (TheMaze.get(WestCell - 1) == "1") {
	                            // East Wall
	                            if (TheMaze.get(WestCell + 1) == "1") {
	                                AvailableCells.add("W"); }
	                        }
	                    }
	                }
	            }
	        }
	        catch (IndexOutOfBoundsException e) {
	            }
	        try {
	            // EastCell
	            if ((CurrCell % x) < (x-2)) {
	                // North wall
	                if (TheMaze.get(EastCell - x) == "1") {
	                    // South Wall
	                    if (TheMaze.get(EastCell + x) == "1") {
	                        // West Wall
	                        if (TheMaze.get(EastCell - 1) == "1") {
	                            // East Wall
	                            if (TheMaze.get(EastCell + 1) == "1") {
	                                AvailableCells.add("E"); }
	                        }
	                    }
	                }
	            }
	        }
	        catch (IndexOutOfBoundsException e) {
	            }       
	        if (AvailableCells.size() > 0) {
	        	int NewCell;
	            String NewCellDir = AvailableCells.get(new Random().nextInt(AvailableCells.size()));
	            if (NewCellDir == "N") {
	                NewCell = NorthCell;
	                TheMaze.set(NorthCell + x, "0"); }
	            else if (NewCellDir == "S") {
	                NewCell = SouthCell;
	                TheMaze.set(SouthCell - x, "0"); }
	            else if (NewCellDir == "W") {
	                NewCell = WestCell;
	                TheMaze.set(WestCell + 1, "0"); }
	            else {
	                NewCell = EastCell;
	                TheMaze.set(EastCell - 1, "0"); }
	            CellStack.push(new int[] { CurrCell });
	            CurrCell = NewCell;
	            VisitedCells += 1; }
	        else {
	        	// At the end we'll have an empty stack, so break out.
	        	if (CellStack.size() == 0) {
	        		break; }
	        	else {
	                CurrCell = CellStack.pop()[0]; }
	        }
	    }
	    return TheMaze; 
	}

}
